…es the sidebar data path
Adds the three remaining sidebar reads to the Rust hot-path crate so the
dashboard's left-rail can skip the cold-start-prone Lambda path on every
nav (Phase 5c follow-up to SMOODEV-1227).
Endpoints:
- GET /v1/organizations — member + parent-admin-managed orgs, tagged with
accessType. Cache key orgs:{user_id}, TTL 60s.
- GET /v1/organizations/:org_id/features — computed feature set mirroring
the TS getOrganizationFeatures precedence chain: overrides trump
defaults trump internal-segment trump Stripe products + metadata.
Cache key features:{org_id}:{user_id}, TTL 60s; features sorted so the
canonical-JSON hash matches Theia's stableStringify on the TS side.
- GET /v1/organizations/:org_id/products — products with embedded order +
stripeProduct relations via jsonb_build_object. Cache key
products:{org_id}:{user_id}, TTL 60s.
All three verify the Supabase JWT via the existing JwksCache and enforce
org membership in SQL (admin pool, no RLS — same trade as /v1/profile).
Per-org endpoints 403 on non-membership.
Adds AppError::Forbidden, three cache key helpers, a product_constants
module that mirrors the TS feature/product maps, and integration tests
covering routing + auth-header parsing for all three. Live-DB
verification stays gated on the Phase 6 shadow harness.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Summary
Wave 2 of the Rust hot-path crate — adds the three remaining sidebar reads so the dashboard's left rail no longer goes through Lambda on every nav. Phase 5c follow-up to SMOODEV-1227.
Endpoints
/v1/organizationspackages/backend/src/routes/organizations.ts(GET list)orgs:{user_id}TTL 60s/v1/organizations/:org_id/featurespackages/backend/src/routes/organization-features.tsfeatures:{org_id}:{user_id}TTL 60s/v1/organizations/:org_id/productspackages/backend/src/routes/products.ts(GET list)products:{org_id}:{user_id}TTL 60sAll three:
JwksCache./v1/profile). Per-org endpoints 403 on non-membership.Feature resolution parity
/v1/organizations/:org_id/featuresmirrors the TSgetOrganizationFeaturesprecedence chain:product_access_overridesrow (active, non-expired) — trumps everything.DEFAULT_ENABLED_FEATURES— always-on.INTERNAL_ONLY_FEATURES+@smoo.airequesting user.PRODUCT_FEATURE_MAP+stripe_products.metadata.features(JSON-encoded string).The product/feature maps are ported into a narrow
product_constantsmodule — the TS side stays the source of truth; the shadow harness from Phase 6 will catch divergence on the next run.Canonical-JSON parity
The
featuresarray is sorted before serialization so the JSON hash matches Theia'sstableStringifyinpackages/backend/src/middleware/shadow-mirror.tswithout an extra Rust-side normalizer. The other two endpoints emit field-by-field JSON that mirrors the DrizzleselectSchema.parse()output shape (camelCase keys, ISO-8601 timestamps).Performance target
p99 < 20ms warm for all three. Not benched in this PR — the shadow harness from Phase 6 will measure against the Lambda baseline. Each endpoint is a 1–3 statement Postgres query (read-only) plus a Redis GET/SET; the warm-path expectation is conservative.
Related: ADR-015 — Rust hot-path service.
Test plan
cargo build --release -p smooai-hot-pathsucceedscargo test -p smooai-hot-path— 17 passed, 1 ignoredcargo clippy -p smooai-hot-path --all-targets -- -D warningscleandocker buildx build --platform linux/arm64 -t smooai-hot-path:test -f rust/hot-path/Dockerfile rust/succeeds🤖 Generated with Claude Code